// 这是基于epoll写的服务器
#pragma once
#include <iostream>
#include <memory>
#include <unordered_map>
#include "Epoller.hpp"
#include "Connection.hpp"
#include "InetAddr.hpp"

using namespace EpollModule;

using connection_t = std::shared_ptr<Connection>;

class Reactor
{
    const static int events_num = 64;//这是我固定的一次性从就绪队列中获取的事件个数

private:
    bool IsConnectionExists(int sockfd)
    {
        return _connection.find(sockfd) != _connection.end();
    }

public:
    Reactor()
        : _isrunning(false), _epoller(std::make_unique<Epoller>())
    {
        _epoller->Init(); // 这个epoll模型可以直接在EpollServer创建时就创建模型。耦合度高而已，但是这是Epoll服务器啊，这是必须的。
    }
    // 不建议在epollServer这里直接加入listen套接字，不利于拓展，耦合度高了。
    
    //  void InsertConnetion(int sockfd,func_t recver, func_t sender, func_t excepter)
    //  {

    // }Reacotr
    void InsertConnetion(connection_t conn)
    {
        auto iter = _connection.find(conn->Fd());
        if (iter == _connection.end())
        {
            // 没找到代表是新链接直接插入进来。
            // 这只是第一步
            // 1.把链接放到unordered_map中进行管理，但是我还需写到epoll中。
            _connection.insert(std::make_pair(conn->Fd(), conn));
            // 2.把新插入的连接插入到epoll中。
            _epoller->Add(conn->Fd(), conn->GetEvents());
            // 3.设置关联关系——让Connection回指当前对象
            conn->SetOwner(this);
        }
    }
    void DelConnection(int sockfd)//异常处理时，要移除管理的连接
    {
        if(IsConnectionExists(sockfd))
        {
            //1.从内核中先移除异常对fd的关心
            _epoller->Delete(sockfd);
            //2.关闭特定的文件描述符，用面向对象的方式关闭，不用CLOSE，换句话就是在对象类中封装close
            _connection[sockfd]->Close();
            //3.再从管理Connection的map中移除
            _connection.erase(sockfd);//以键值移除
            
        }
    }
    void EnableReadWrite(int sockfd,bool readable,bool writeable)//开启或关闭对sockfd对写或读事件的关心。
    {
        //首先得合法存在
        if(IsConnectionExists(sockfd))
        {
            uint32_t events = ((readable?EPOLLIN:0) |  (writeable?EPOLLOUT:0) |EPOLLET);
            _connection[sockfd]->SetEvents(events);
            //还需要写到内核中
            _epoller->Updata(sockfd,_connection[sockfd]->GetEvents());
        }
    }
    void DebugPrint()
    {
        std::cout<<"Epoller 管理的fd："<<std::endl;
        for(auto e : _connection)
        {
            std::cout<<e.first<<":";
        }
        std::cout<<std::endl;
    }
    //这就是基于事件驱动的事件派发器。
    void Distribution(int n)//事件派发的功能，用函数封装
    {
        //对就绪的事件做派发
         for (int i = 0; i < n; i++)
        {   
            DebugPrint();
            // 这里可以给已经就绪的事件派发了。
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;
            if ((revents & EPOLLERR) || (revents & EPOLLHUP)) // 出错和挂了。
                revents = (EPOLLIN | EPOLLOUT);               // 异常事件，转化称为读写事件，这样所有的异常函数都可以集中到一个函数中。
            if ((revents & EPOLLIN) && IsConnectionExists(sockfd))
            {
                // _connection[sockfd]->CallRecv(_connection[sockfd]); // 这里怎么区分listensockfd和普通fd呢看Connection，因为Connection是fd与套接字的映射。刚好我从wait中拿到的就是一个一个含有fd的结构体，所以不同fd对应不同的功能已经是设定好了的，最后通过k-v拿到的函数就一定是我们要的咯！！
                _connection[sockfd]->Recver(); 
            }
            if ((revents & EPOLLOUT) && IsConnectionExists(sockfd)) // 一个事件即可能是读事件也可能是写事件，防止在处理其中一个事件时异常导致fd关闭了，所以要判断fd是否关闭，连接是否还存在。
            {
                // _connection[sockfd]->CallSend();
                _connection[sockfd]->Sender();
            }
        }
    }
    void LoopOnce(int timeout)//不断的把功能细化函数模块化，就变的更加清晰，可读性更高！！！！！
    {
        int n = _epoller->Wait(_revs, events_num,timeout); // 用epoller封装了epoll模型
        Distribution(n);
    }

    void Loop()
    {
        Start();
        // 如何监视fd呢？
        int timeout=-1;
        while (_isrunning)
        {
            LoopOnce(timeout);//方便加入其他的函数功能 
        }

        Stop();
    }
    void Stop()
    {
        _isrunning = false;
    }
    void Start()
    {
        _isrunning = true;
    }
    Reactor() {}

private:
    std::unique_ptr<Epoller> _epoller;                 // 这是Reactor底层负责监视IO的模型。注：这只是指针，没有初始化就是空的，还得new一个。
    std::unordered_map<int, connection_t> _connection; // 这是有必要的，因为我们在外层我们必要的要去管理/知道当前服务器内部所有的连接。
    bool _isrunning;
    struct epoll_event _revs[events_num]; // 事件的结构体，来获取事件
};